import { observer } from "./observer/index";
import { nextTick } from "./utils/nextTick";
import Watcher from "./observer/watcher";

export function initState(vm) {
  let ops = vm.$options
  if (ops.props) {
    initProps(vm);
  }
  if (ops.data) {
    initData(vm);
  }
  if (ops.methods) {
    initMethods(vm);
  }
  if (ops.computed) {
    initComputed(vm);
  }
  if (ops.watch) {
    initWatch(vm);
  }
}

function initProps(vm) {

}

function initMethods(vm) {
  let methods = vm._methods = vm.$options['methods'];
  // 将methods上的方法全部代理到vm上
  for (let key in methods) {
    proxy(vm, "_methods", key);
  }
}

let sharedPropDefinition = {};
function defineComputed(target, key, userDef) {
  sharedPropDefinition = {
    enumerable: true,
    configurable: true,
    get: () => { },
    set: () => { }
  }
  // 使用高阶函数实现缓存机制
  sharedPropDefinition.get = createComputedGetter(key);
  if (typeof userDef !== "function") {
    sharedPropDefinition.set = userDef.set;
  }
  // 将计算属性代理到实例对象上
  Object.defineProperty(target, key, sharedPropDefinition);
}

// 高阶函数(实际就是返回一个方法，这个方法里面用dirty开关判断是否要执行用户定义的计算方法)
function createComputedGetter(key) {
  return function () {
    let watcher = this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      return watcher.value;
    }
  }
}

function initComputed(vm) {
  let computed = vm.$options['computed'];
  let watcher = vm._computedWatchers = {};
  for (let key in computed) {
    let userDef = computed[key];
    let getter = typeof userDef === "function" ? userDef : userDef.get;
    // 每一个计算属性都需要一个计算watcher
    watcher[key] = new Watcher(vm, getter, () => { }, { lazy: true }); // 计算属性不使用，不执行
    defineComputed(vm, key, userDef);
  }
}

function initWatch(vm) {
  let watch = vm.$options.watch;
  for (let key in watch) {
    // 判断属性对应的值的类型(函数、对象、数组、字符串)
    let handler = watch[key];
    if (Array.isArray(handler)) {
      handler.forEach(item => {
        createrWatcher(vm, key, item);
      })
    } else {
      createrWatcher(vm, key, handler);
    }
  }
}

function createrWatcher(vm, exprOrfn, handler, options) {
  if (typeof handler === 'object') {
    options = handler;
    handler = handler.handler;
  }
  if (typeof handler === 'string') {
    handler = vm[handler];
  }
  // typeof handler === 'function'，若是简单的函数形式，就直接走到下面这一行
  return vm.$watch(exprOrfn, handler, options);
}

function initData(vm) {
  let str = Object.prototype.toString.call(vm.$options.data).slice(8, -1);
  let data = vm._data = str === "Object" ? vm.$options.data : vm.$options.data.call(vm);
  // 将data上的属性全部代理到vm上
  for (let key in data) {
    proxy(vm, "_data", key);
  }
  // 数据劫持 (上面的for循环只是Object.defineProperty了data最外面的一层)
  // 下面是进行深层次的Object.defineProperty
  observer(data);
}

function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key];
    },
    set(newVal) {
      vm[source][key] = newVal;
    }
  })
}

export function stateMixin(Vue) {
  Vue.prototype.$nextTick = function (cb) {
    nextTick(cb);
  }
  Vue.prototype.$watch = function (exprOrfn, handler, options = {}) {
    // 实现watch的核心就是要使用new Watcher()，监听 Watcher
    let watcher = new Watcher(this, exprOrfn, handler, { ...options, user: true });
    if (options && options.immediate) {
      handler.call(this, watcher.value, undefined);
    }
  }
}